#ifndef __STENCIL_H__
#define __STENCIL_H__

#include "cnc/cnc.h"
#include "cnc/default_tuner.h"

using namespace std;

#include "cnc/debug.h"

// forward declaration
template <int TILETAGDIM, int INTERINTERTASKDIM>
class stencil_context;

#include "TileTag.hpp"

// Stencil core class
template <int TILETAGDIM, int INTERINTERTASKDIM> class stencil_step {
public:
  int64_t execute
  (const TileTag<TILETAGDIM, INTERINTERTASKDIM> &, 
   stencil_context<TILETAGDIM, INTERINTERTASKDIM> &) const;
};

// Stencil tuner class
template <int TILETAGDIM, int INTERINTERTASKDIM> class stencil_tuner : 
  public CnC::step_tuner<>, public CnC::hashmap_tuner {
public:
  stencil_tuner() {}
  template< class DC >
  void depends(const TileTag<TILETAGDIM, INTERINTERTASKDIM> &, 
               stencil_context<TILETAGDIM, INTERINTERTASKDIM> &, DC &) const;
};


// Context for a single stencil 
template <int TILETAGDIM, int INTERINTERTASKDIM> class stencil_context : 
public CnC::context< stencil_context<TILETAGDIM, INTERINTERTASKDIM> > {
public:
  stencil_tuner <TILETAGDIM, INTERINTERTASKDIM> m_tuner;

  CnC::item_collection< TileTag<TILETAGDIM, INTERINTERTASKDIM>, 
                        void *, stencil_tuner<TILETAGDIM, INTERINTERTASKDIM> > M;

  CnC::step_collection< stencil_step<TILETAGDIM, INTERINTERTASKDIM>, 
                        stencil_tuner<TILETAGDIM, INTERINTERTASKDIM> 
                        > m_steps;

  CnC::tag_collection< TileTag<TILETAGDIM, INTERINTERTASKDIM>// , 
                       // stencil_tuner<TILETAGDIM, INTERINTERTASKDIM> 
                       > m_tags;

  stencil_context() : 
    m_tuner(), 
    M(*this, "M", m_tuner), 
    m_tags(*this), m_steps(*this) {

    // prescription
    m_tags.prescribes( m_steps, *this );

    // producer/consumer
    m_steps.consumes( M );
    m_steps.produces( M );
  }

};


#endif
